/* 
 * File:   Downloader.cpp
 * Author: Lance
 * 
 * Created on September 13, 2012, 10:17 PM
 */

#include "Downloader.h"

const string HOST("HOST");
const string HEAD("HEAD");
const string C_LENGTH("Content-Length");
const string RANGE("Range");

using namespace std;
struct d_thread
{
	HTTPRequest * d_request;
	HTTPResponse * d_response;
	Socket * d_socket;
	d_thread()
	{
		d_request = NULL;
		d_response = NULL;
		d_socket = NULL;
	}
	~d_thread()
	{
		if (d_request != NULL)
			delete d_request;
		if (d_response != NULL)
			delete d_response;
		if (d_socket != NULL)
			delete d_socket;
	}
};

/*
 * pthread helper function. takes a pointer to a HTTPRequest (will destroy request)
 * returns a pointer to an HTTPResponse withthe message stored in "ResponseMessage" header
 */
void * download(void * ptr){
	d_thread * requestData = (d_thread*) ptr;
	requestData->d_socket = new Socket(requestData->d_request->header(HOST), 80);
	Socket * serverConn = requestData->d_socket;
	serverConn->sendRequest(requestData->d_request);
	
	if(requestData->d_request->method().compare(HEAD) == 0)
	{
		requestData->d_response = serverConn->getResponseHeader();
	}
	else
	{
		requestData->d_response = serverConn->getResponse();
	}
	
	requestData->d_socket->closeSocket();	
}

Downloader::Downloader(string url, int numThreads) {
	this->numThreads = numThreads;
	this->url = url;
	fileHost.parse(url);
}

Downloader::Downloader(const Downloader& orig) {
}

Downloader::~Downloader() {

}

void Downloader::startDownload() {
	//Head request to get content length
	string path = fileHost.path();
	string host = fileHost.host();
	d_thread * requestData = new d_thread();
	HTTPRequest * request = new HTTPRequest();
	request->uri(path);
	request->header(HOST, host) ;
	request->method(HEAD);
	//construct requests based on header response
	requestData->d_request = request;
	download(requestData);
	HTTPResponse * response;
	response = requestData->d_response;
	
	if(response->header("Content-Length").compare("") == 0)
	{
		cout << "Error: no Content-Length header" << endl;
		return;
	}
//	return;
	int bytes = atoi(response->header(C_LENGTH).c_str());
	int chunkSize = bytes/numThreads;
	int chunkRemainder = atoi(response->header(C_LENGTH).c_str())%numThreads;
	int chunkStart = 0;

	pthread_t threads[numThreads];
	d_thread * data[numThreads];
	struct timespec ts_start;
	clock_gettime(CLOCK_MONOTONIC, &ts_start);
	double start_time = ts_start.tv_sec + ts_start.tv_nsec/1000000000.0;

	for(int i = 0; i < numThreads; i++)
	{
		data[i] = new d_thread();
		d_thread * requestData = data[i];

		int requestSize = chunkSize;
		if(i == 0)
		{
			requestSize += chunkRemainder;
		}
		string start = static_cast<ostringstream*>( &(ostringstream() << chunkStart) )->str();
		string end = static_cast<ostringstream*>( &(ostringstream() << (chunkStart+requestSize-1)) )->str();
		chunkStart += requestSize;
		
		
		HTTPRequest * threadRequest = new HTTPRequest();
		threadRequest->uri(path);
		threadRequest->header(HOST, host);
		threadRequest->header(RANGE, "bytes="+start+"-"+end);
		requestData->d_request = threadRequest;
		pthread_create(&threads[i], NULL, &download, requestData);
	}
	
	//Make http request hand it to the download function
	
	for(int i = 0; i < numThreads; i++)
	{
		pthread_join(threads[i], NULL);
	}
	
	ofstream myfile;
	myfile.open(fileHost.file().c_str());
	for(int i = 0; i < numThreads; i++)
	{
		myfile << data[i]->d_response->header("ResponseMessage");
	}
	struct timespec ts_stop;
	clock_gettime(CLOCK_MONOTONIC, &ts_stop);
	double stop_time = ts_stop.tv_sec + ts_stop.tv_nsec/1000000000.0;

	myfile.close();
	
	for(int i = 0; i < numThreads; i++)
	{
		delete data[i];
	}
	
	double secs = stop_time - start_time;
	string url = fileHost.url();
	cout << url << " " << numThreads << " " << bytes <<  " " << secs << endl;

}

